/*********************************************************************************
 *      Copyright:  (C) 2023 iot<iot@email.com>
 *                  All rights reserved.
 *
 *       Filename:  socket.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(04/20/23)
 *         Author:  iot <iot@email.com>
 *      ChangeLog:  1, Release initial version on "04/20/23 20:23:37"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/tcp.h>

#include "socket.h"
#include "logger.h"

int socket_init(socket_t *socket,char *hostname,int port)
{
    socket->socketfd = -1;
    socket->port = port;
    strcpy(socket->hostname,hostname);
    socket->status = 0;

    return 0;
}

int socket_connect(socket_t *socket)
{
    struct sockaddr_in      serv_addr;
    struct sockaddr_in      *addr;
    struct in_addr          inaddr;
    struct addrinfo         hints;
    struct addrinfo         *ret;
    struct addrinfo         ip;

    memset(&hints,0,sizeof(hints));
    hints.ai_family = AF_INET; //IPV4
    hints.ai_flags = AI_PASSIVE; //AI_PASSIVE当此标志置位时，表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时，表示将在connect()函数调用中使用
    htons.ai_socktype = SOCK_STREAM; //TCP

    if( inet_aton(socket->hostname,&inaddr) )
    {
        hints.ai_flags = AI_NUMERICHOST; //AI_NUMERICHOST当此标志置位时，此标志表示调用中的节点名必须是一个数字地址字符串
    }

    if( getaddrinfo(socket->hostname,NULL,&hints,&ret) != 0 )
    {
        log_warning("getaddrinfo() failure\n");
        return -1;
    }

    memset(&serv_addr,0,sizeof(serv_addr));
    
    for( ip=res;ip!=NULL;ip=ip->ai_next )
    {
        addr = (struct sockaddr_in*)ip->ai_addr;
        log_info("ip address:%s\n",inet_ntoa(addr->sin_addr));

        if( (socket->socketfd = socket(AF_INET,SOCK_STREAM,0)) < 0 )
        {
            log_warning("creat socket failure:%s\n",strerror(errno));
            freeaddrinfo(ret);
            return -2;
        }
        log_info("creat socket[%d] successfully\n",socket->socketfd);

        serv_addr.sin_family = AF_INET;
	    serv_addr.sin_port = htons(socket->port);

        if( connect(socket->socketfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0 )
        {
            log_warning("connect server[%s,%d] failure:%s\n",socket->socketfd,socket->port,strerror(errno));
            socket_close(socket);
        }
        else
        {
            log_info("connect server[%s,%d] successfully\n",socket->socketfd,socket->port);
            socket->status = 1;
            break;
        }

    }

    return socket->socketfd;	
}

int socket_state(socket_t *socket)
{
	struct tcp_info		info;
	int					len;

	len = sizeof(info);
    socket->status = 0; 

	if( socket->socketfd  <= 0 )
	{
		return -1;
		log_info("socket disconnected\n");
	}

	getsockopt(socket->socketfd,IPPROTO_TCP,TCP_INFO,&info,(socklen_t *)& len);
	if( (info.tcpi_state == 1) )
	{
        socket->status = 0;
		log_info("socket connected\n");
        return 1;
	}
	else
	{
		return -1;
		log_info("socket disconnected\n");
	}

}

void socket_close(socket_t *socket)
{
    close(socket->socketfd);
    socket->status = 0;
}
